#include "gpio.h"

#include <fcntl.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define GPIO_EXPORT "/sys/class/gpio/export"
#define GPIO_UNEXPORT "/sys/class/gpio/unexport"
#define GPIO_DIRECTION "/sys/class/gpio/gpio%d/direction"
#define GPIO_VALUE "/sys/class/gpio/gpio%d/value"
#define GPIO_EDGE "/sys/class/gpio/gpio%d/edge"
#define GPIO_ACTIVE_LOW "/sys/class/gpio/gpio%d/active_low"

GPIO::GPIO(int A, int B, QObject *parent) : QObject(parent), a(A), b(B)
{
}

inline int GPIO::Pins(int A, int B)
{
    return (A * 32 + B);
}

/*************************************************************************************************************************
 *函数        			:   int gpio_export(int pin)
 *功能        			:   打开引脚pin的gpio
 *参数        			:   pin: gpio号
 *返回        			:	成功返回0;失败返回-1
 *依赖        			:	gpio未被打开
 *说明        			:
 *************************************************************************************************************************/
int GPIO::gpioExport(int pin)
{
    char buffer[64];
    int  len;
    int  fd;

    fd = open(GPIO_EXPORT, O_WRONLY);
    if (fd < 0)
    {
        printf("Failed to open export for writing!\n");
        return (-1);
    }

    len = snprintf(buffer, sizeof(buffer), "%d", pin);
    if (write(fd, buffer, len) < 0)
    {
        printf("Failed to export gpio!\n");
        return -1;
    }

    close(fd);
    return 0;
}

/*************************************************************************************************************************
 *函数        			:   int gpioUnexport(int pin)
 *功能        			:   释放引脚pin的gpio
 *参数        			:   pin: gpio号
 *返回        			:	成功返回0;失败返回-1
 *依赖        			:	pin gpio已经打开
 *说明        			:
 *************************************************************************************************************************/
int GPIO::gpioUnexport(int pin)
{
    char buffer[64];
    int  len;
    int  fd;

    fd = open(GPIO_UNEXPORT, O_WRONLY);
    if (fd < 0)
    {
        printf("Failed to open unexport for writing!\n");
        return -1;
    }

    len = snprintf(buffer, sizeof(buffer), "%d", pin);
    if (write(fd, buffer, len) < 0)
    {
        printf("Failed to unexport gpio!");
        return -1;
    }

    close(fd);
    return 0;
}

/*************************************************************************************************************************
 *函数        			:   int gpioSetDirection(int pin, int direction)
 *功能        			:   设置gpio方向
 *参数        			:   pin: gpio号；direction: 输入为0 ，输出为非零
 *返回        			:	成功返回0;失败返回-1
 *依赖        			:	pin gpio已经打开
 *说明        			:
 *************************************************************************************************************************/
int GPIO::gpioSetDirection(int pin, int direction)
{
    static const char dir_str[] = "in\0out";
    char              path[64];
    int               fd;

    snprintf(path, sizeof(path), GPIO_DIRECTION, pin);
    fd = open(path, O_WRONLY);
    if (fd < 0)
    {
        printf("Failed to open gpio direction for writing!\n");
        return -1;
    }

    if (write(fd, &dir_str[direction == 0 ? 0 : 3], direction == 0 ? 2 : 3) < 0)
    {
        printf("Failed to set direction!\n");
        return -1;
    }

    close(fd);
    return 0;
}

/*************************************************************************************************************************
 *函数        			:   int gpioReadDirection(int pin,char *dir)
 *功能        			:   读取gpio方向
 *参数        			:   pin: gpio号；dir: 寄存gpio方向
 *返回        			:	成功返回0;失败返回-1
 *依赖        			:	pin gpio已经打开
 *说明        			:
 *************************************************************************************************************************/
int GPIO::gpioReadDirection(int pin, char *dir)
{
    char  path[64];
    int   fd;
    char *pdir = dir;

    snprintf(path, sizeof(path), GPIO_DIRECTION, pin);
    fd = open(path, O_RDONLY);
    if (fd < 0)
    {
        printf("Failed to open gpio direction for reading!\n");
        return -1;
    }

    read(fd, pdir, 3);

    if (*pdir == 'i')
    {
        *(pdir + 2) = '\0';
    }
    else if (*pdir == 'o')
    {
        *(pdir + 3) = '\0';
    }

    close(fd);

    return 0;
}

/*************************************************************************************************************************
 *函数        			:   int gpioSetValue(int pin, int value)
 *功能        			:   设置gpio输出值
 *参数        			:   pin: gpio号；value:0 输出低电平、 1 输出高电平
 *返回        			:	成功返回0, 失败返回-1
 *依赖        			:	gpio方向为输出
 *说明        			:
 *************************************************************************************************************************/
int GPIO::gpioSetValue(int pin, int value)
{
    static const char values_str[] = "01";
    char              path[64];
    int               fd;

    snprintf(path, sizeof(path), GPIO_VALUE, pin);
    fd = open(path, O_WRONLY);
    if (fd < 0)
    {
        //        printf("Failed to open gpio value for writing!\n");
        return -1;
    }

    if (write(fd, &values_str[value == 0 ? 0 : 1], 1) < 0)
    {
        printf("%d,Failed to write value!\n", __LINE__);
        return -1;
    }

    close(fd);
    return 0;
}

/*************************************************************************************************************************
 *函数        			:   int gpioRead(int pin)
 *功能        			:   读取gpio value
 *参数        			:   pin: gpio号
 *返回        			:	成功返回value; 失败返回-1
 *依赖        			:	无
 *说明        			:
 *************************************************************************************************************************/
int GPIO::gpioReadValue(int pin)
{
    char path[64];
    char value_str[3];
    int  fd;

    snprintf(path, sizeof(path), GPIO_VALUE, pin);
    fd = open(path, O_RDONLY);
    if (fd < 0)
    {
        //        printf("Failed to open gpio value for reading!\n");
        return -1;
    }

    if (read(fd, value_str, 3) < 0)
    {
        printf("Failed to read value!\n");
        return -1;
    }

    close(fd);
    return (atoi(value_str));
}

/*************************************************************************************************************************
 *函数        			:   int gpioSetEdge(int pin, int edge)
 *功能        			:   设置中断触发模式
 *参数        			:   pin: gpio号； edge: 中断触发模式
 *返回        			:	成功返回
 *依赖        			:	无
 *说明        			:   edge 这个控制中断触发模式
 *        			    :        none      无：      echo none > edge
 *        			    :        rising    上升沿触发：echo rising > edge
 *        			    :        falling   下降沿触发：echo falling > edge
 *        			    :        both      轮询：     echo both > edge
 *************************************************************************************************************************/
int GPIO::gpioSetEdge(int pin, int edge)
{
    const char dir_str[] = "none\0rising\0falling\0both";
    int        ptr;
    char       path[64];
    int        fd;

    switch (edge)
    {
        case 0:
            ptr = 0;
            break;
        case 1:
            ptr = 5;
            break;
        case 2:
            ptr = 12;
            break;
        case 3:
            ptr = 20;
            break;
        default:
            ptr = 0;
    }

    snprintf(path, sizeof(path), GPIO_EDGE, pin);
    fd = open(path, O_WRONLY);
    if (fd < 0)
    {
        printf("Failed to open gpio edge for writing!\n");
        return -1;
    }

    if (write(fd, &(dir_str[ptr]), strlen(&(dir_str[ptr]))) < 0)
    {
        printf("Failed to set edge!\n");
        return -1;
    }

    close(fd);
    return 0;
}

/*************************************************************************************************************************
 *函数        			:   int gpioReadEdge(int pin,char *edge)
 *功能        			:   读取gpio中断方式
 *参数        			:   pin: gpio号；edge: 寄存gpio中断模式
 *返回        			:	成功返回0;失败返回-1
 *依赖        			:
 *说明        			:
 *************************************************************************************************************************/
int GPIO::gpioReadEdge(int pin, char *edge)
{
    char  path[64];
    int   fd;
    char *pedge = edge;

    snprintf(path, sizeof(path), GPIO_EDGE, pin);
    fd = open(path, O_RDONLY);
    if (fd < 0)
    {
        printf("Failed to open gpio edge!\n");
        return -1;
    }  //	gpioSetValue(DREQ, 0);

    read(fd, pedge, 7);

    switch (*pedge)
    {
        case 'n':
        case 'b':
            *(pedge + 4) = '\0';
            break;
        case 'r':
            *(pedge + 6) = '\0';
            break;
        case 'f':
            *(pedge + 7) = '\0';
            break;
        default:

            break;
    }

    close(fd);

    return 0;
}

/*************************************************************************************************************************
 *函数        			:   int gpioSetActiveLow(int pin, int low)
 *功能        			:   设置gpio active_low.用于决定value中的值是否翻转。
 *参数        			:   pin: gpio号；low:0不翻转，1翻转
 *返回        			:	成功返回0, 失败返回-1
 *依赖        			:
 *说明        			:
 *************************************************************************************************************************/
int GPIO::gpioSetActiveLow(int pin, int low)
{
    static const char low_str[] = "01";
    char              path[64];
    int               fd;

    snprintf(path, sizeof(path), GPIO_ACTIVE_LOW, pin);
    fd = open(path, O_WRONLY);
    if (fd < 0)
    {
        printf("Failed to open active_low!\n");
        return -1;
    }

    if (write(fd, &low_str[low == 0 ? 0 : 1], 1) < 0)
    {
        printf("Failed to write active_low!\n");
        return -1;
    }

    close(fd);
    return 0;
}

/*************************************************************************************************************************
 *函数        			:   int gpioReadActiveLow(int pin)
 *功能        			:   读取gpio active_low
 *参数        			:   pin: gpio号
 *返回        			:	成功返回active_low; 失败返回-1
 *依赖        			:
 *说明        			:
 *************************************************************************************************************************/
int GPIO::gpioReadActiveLow(int pin)
{
    char path[64];
    char low_str[3];
    int  fd;

    snprintf(path, sizeof(path), GPIO_ACTIVE_LOW, pin);
    fd = open(path, O_RDONLY);
    if (fd < 0)
    {
        printf("Failed to open gpio active_low for reading!\n");
        return -1;
    }

    if (read(fd, low_str, 3) < 0)
    {
        printf("Failed to read active_low!\n");
        return -1;
    }

    close(fd);
    return (atoi(low_str));
}

/*************************************************************************************************************************
 *函数        			:   int gpioGetState(int pin,char *dir)
 *功能        			:   打印gpio状态
 *参数        			:   pin: gpio号
 *返回        			:	成功返回active_low; 失败返回-1
 *依赖        			:
 *说明        			:
 *************************************************************************************************************************/
int GPIO::gpioGetState(int pin, char *dir)
{
    gpioReadDirection(pin, dir);
    printf("gpio%d direction is %s\n", pin, dir);
    printf("gpio%d value is %d\n", pin, gpioReadValue(pin));

    return 0;
}

/*************************************************************************************************************************
 *函数        			:   int gpioOpenValueFileRD(int pin)
 *功能        			:   打开gpio value文件
 *参数        			:   pin: gpio号
 *返回        			:	成功返回value文件ID, 失败返回-1
 *依赖        			:
 *说明        			:
 *************************************************************************************************************************/
int GPIO::gpioOpenValueFileRD(int pin)
{
    char path[64];
    int  fd;

    snprintf(path, sizeof(path), GPIO_VALUE, pin);
    fd = open(path, O_RDONLY);
    if (fd < 0)
    {
        //        printf("Failed to open gpio value for reading!\n");
        return -1;
    }
    else
    {
        return fd;
    }
}

/*************************************************************************************************************************
 *函数        			:   int gpioOpenValueFileWR(int pin)
 *功能        			:   打开gpio value文件
 *参数        			:   pin: gpio号
 *返回        			:	成功返回value文件ID, 失败返回-1
 *依赖        			:
 *说明        			:
 *************************************************************************************************************************/
int GPIO::gpioOpenValueFileWR(int pin)
{
    char path[64];
    int  fd;

    snprintf(path, sizeof(path), GPIO_VALUE, pin);
    fd = open(path, O_WRONLY);
    if (fd < 0)
    {
        //        printf("Failed to open gpio value for writing!\n");
        return -1;
    }
    else
    {
        return fd;
    }
}
/*************************************************************************************************************************
 *函数        			:   int gpioCloseValueFile(int fd)
 *功能        			:   关闭gpio value文件
 *参数        			:   fd: gpio value文件ID
 *返回        			:	返回0
 *依赖        			:	gpio value文件已打开
 *说明        			:
 *************************************************************************************************************************/
int GPIO::gpioCloseValueFile(int fd)
{
    close(fd);
    return 0;
}

/*************************************************************************************************************************
 *函数        			:   int gpioOnlySetValue(int fd, int value)
 *功能        			:   只设置gpio输出值
 *参数        			:   fd: gpio value文件ID；value:0 输出低电平、 1 输出高电平
 *返回        			:	成功返回0, 失败返回-1
 *依赖        			:	文件打开，gpio方向为输出
 *说明        			:
 *************************************************************************************************************************/
int GPIO::gpioOnlySetValue(int fd, int value)
{
    static const char values_str[] = "01";

    if (write(fd, &values_str[value == 0 ? 0 : 1], 1) < 0)
    {
        printf("Failed to write value!\n");
        return -1;
    }

    return 0;
}

/*************************************************************************************************************************
 *函数        			:   int gpioOnlyReadValue(int fd)
 *功能        			:   只读取gpio value
 *参数        			:   fd: gpio value文件ID
 *返回        			:	成功返回value; 失败返回-1
 *依赖        			:	文件打开
 *说明        			:
 *************************************************************************************************************************/
int GPIO::gpioOnlyReadValue(int fd)
{
    char value_str[3];

    if (read(fd, value_str, 3) < 0)
    {
        printf("Failed to read value!\n");
        return -1;
    }

    return (atoi(value_str));
}

int GPIO::Configuration(DirectionType direction, ActiveLowType activelow)
{
    int pin  = Pins(a - 1, b);
    int dire = 0;
    int low  = 0;

    if (a > 0)
    {
        if (direction == Input)
        {
            dire = 0;  //输入
        }
        else if (direction == OutPut)
        {
            dire = 1;  //输出
        }

        if (activelow == NoFlip)
        {
            low = 0;  //不翻转
        }
        else if (activelow == Flip)
        {
            low = 1;  //翻转
        }

        gpioUnexport(pin);
        gpioExport(pin);
        gpioSetDirection(pin, dire);
        //        if (direction == OutPut)
        //        {
        //            gpioSetValue(pin, 0);
        //        }
        gpioSetActiveLow(pin, low);

        return 0;
    }
    else
    {
        return -1;
    }
}

void GPIO::SetValue(ValueType value)
{
    int pin = Pins(a - 1, b);
    gpioSetValue(pin, value);
}

ValueType GPIO::ReadValue(void)
{
    int pin = Pins(a - 1, b);
    return ValueType(gpioReadValue(pin));
}
